from sqlalchemy.ext.declarative.clsregistry import _MultipleClassMarker
from sqlalchemy.orm import sessionmaker, state

from apps.a_common.error import DBError
from apps.a_common.log import MyLogger
from apps.foundation import db
from utils.time import int_timestamp

"""
约定
2. 所有varchar字段，都是用db.VARCHAR(126)，这样扩展强，性能消耗小（在PG SQL下）
3. 所有存外面的数据(比如OSS，NOSQL，文件系统）的变量，都用**_store_id
4. 所有的bool字段，变量名都为is_***
5. 所有时间字段，都用db.Integer
6. 尽量使用server_default，server_default=字符串。
7. 所有的ARRAY字段，变量名都为**_line
8. 所有的图片类型，变量名都为**_image
9. 所有的查询，都使用filter，而不使用filter_by
10. 将常用查询尽可能封装在Model类内部
"""

log = MyLogger('BASE MODEL')


def safe_commit():
    try:
        db.session.commit()
    except Exception as e:
        log.warning("DB sql commit error, detail: {}".format(e))
        db.session.rollback()
        raise DBError()


def db_flush_all(*args):
    if args:
        db.session.add_all(args)
        db.session.flush()


def db_flush(instance):
    db.session.add(instance)
    db.session.flush()


class BaseModel(db.Model):
    __abstract__ = True
    id = db.Column(db.Integer, primary_key=True, autoincrement=True, nullable=True)

    def promise_id(self):
        if self.id is None:
            db_flush(self)

    def to_dict(self):
        # 读一下id属性，从而确保所有属性正确读入(载入)
        _id = self.id
        data_properties = {
            k: v
            for k, v in list(self.__dict__.items()) if
            k not in self.__undict__ and not isinstance(v, state.InstanceState)
        }
        data_properties.update({
            k: getattr(self, k)
            for k, f in list(self.__class__.__dict__.items())
            if (k not in self.__undict__) and isinstance(f, property)
        })
        return data_properties


class TimeMarkModel(BaseModel):
    __abstract__ = True
    create_at = db.Column(db.Integer, comment='创建时间', default=int_timestamp)
    update_at = db.Column(db.Integer, comment='更新时间', onupdate=int_timestamp, default=int_timestamp)


class DynamicModelMixin(object):
    @classmethod
    def _create(cls, tb_suffix, auto_create_table=True):
        # 1.tb_suffix和cls，生成表名字
        # 2.搜索当前对象中的声明类是否有该类，如果有，直接使用其创建对象，否则尝试生成新类，创建数据库表并用新类创建对象
        # tb_keys = [cls.__name__.lower(), tb_suffix]
        # tb_name = '_'.join(filter(lambda x: x != '', tb_keys))
        tb_name = cls.__name__.lower()
        if tb_suffix != "":
            tb_name = "%s_%s" % (tb_name, str(tb_suffix))
        tb_cls = None
        if tb_name in db.Model._decl_class_registry:
            if isinstance(db.Model._decl_class_registry[tb_name], _MultipleClassMarker):
                tb_cls = list(db.Model._decl_class_registry[tb_name].contents)[0]()
            else:
                tb_cls = db.Model._decl_class_registry[tb_name]
        if tb_cls is None:
            if hasattr(cls, '__bind_key__'):
                db_engine = db.get_engine(bind=cls.__bind_key__)
            else:
                db_engine = db.get_engine()
            tb_exists = db_engine.dialect.has_table(db_engine, tb_name)
            if tb_exists or auto_create_table:
                metadata = {
                    '__tablename__': tb_name,
                    '__tb_suffix__': tb_suffix,
                    '__table_args__': {
                        'extend_existing': True
                    }
                }
                tb_cls = type(tb_name, (db.Model, cls), metadata)
                if not tb_exists:
                    tb_cls.__table__.create(bind=db_engine)
        return tb_cls

    @classmethod
    def check_on(cls):
        """ 检查一个动态表有没有on成具体的一个表 """
        table_name = getattr(cls, '__tablename__', None)
        if table_name is None:
            raise Exception('DynamicModel used before on')
        else:
            return table_name

    @classmethod
    def on(cls, suffix, auto_create_table=True):
        m = cls._create(suffix, auto_create_table)
        return m

    @classmethod
    def on_date(cls, suffix, date, auto_create_table=True):
        m = cls._create('{}_{}'.format(suffix, date), auto_create_table)
        return m


def get_table_session(table):
    """ 获得对应表下的session """
    if getattr(table, '__bind_key__', None):
        engine = db.get_engine(bind=table.__bind_key__)
        session_maker = sessionmaker(engine)
        session = session_maker()
    else:
        session = db.sessionmaker()()
    return session
